#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"

#include "mywifi.h"

#define SSID_KEY "wifi_ssid"
#define PSWD_KEY "wifi_pswd"

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;
static int s_retry_num = 0;
static const char *TAG = "wifi station";

wifi_config_t wifi_config = {
    .sta = {
        // .ssid = {'4','0','2',,'p','u','b','l','i','c'},
        .ssid = {'4', '0', '2', '1', '1'},
        .password = {'4', '0', '2', '6', '6', '6', '6', '6', '6'},
        /* Setting a password implies station will connect to all security modes including WEP/WPA.
         * However these modes are deprecated and not advisable to be used. Incase your Access point
         * doesn't support WPA2, these mode can be enabled by commenting below line */
        .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        .pmf_cfg = {
            .capable = true,
            .required = false},
    },
};

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            // she zhi
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
            xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
            esp_wifi_stop();
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        store_wificonfig_to_nvs();
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

uint8_t wifi_init(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));

    start_sta_wifi();

    ESP_LOGI(TAG, "esp_wifi_start finished.");
    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    return 0;
}

uint8_t get_wifi_status(void)
{
    if (xEventGroupGetBits(s_wifi_event_group) & WIFI_CONNECTED_BIT)
    {
        // ESP_LOGI(TAG, "WIFI Connected\n");
        return CONN_OK;
    }
    else if (xEventGroupGetBits(s_wifi_event_group) & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "WIFI Disconnected\n");
        return CONN_FAIL;
    }
    else if (xEventGroupGetBits(s_wifi_event_group) & 0)
    {
        ESP_LOGI(TAG, "WIFI Disconnected\n");
        return CONN_NONE;
    }
    else if (xEventGroupGetBits(s_wifi_event_group) & (WIFI_FAIL_BIT | WIFI_CONNECTED_BIT))
    {
        ESP_LOGI(TAG, "WIFI Disconnected\n");
        return CONN_BOTH;
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        return CONN_ELTH;
    }
}

// 重置wifi状态
void reset_wifi(void)
{
    uint8_t connRet = 0;
    connRet = get_wifi_status();
    if (connRet == CONN_OK)
    {
        // esp_wifi_disconnect();
        esp_wifi_stop();
        ESP_LOGE(TAG, "stop wifi !!!!!!!!!!!!!!\n");
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
        // xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
    }
    else if (connRet == CONN_FAIL)
    {
        xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
    }
    else if (connRet == CONN_BOTH)
    {
        xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
        xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
    else if (connRet == CONN_NONE)
    {
        // xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
    }
    else
    {
        ESP_LOGI(TAG, "UNEXPECTED EVENT wifi\n");
    }
}

void start_sta_wifi(void)
{
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}

uint8_t app_wifi_wait_connected(void)
{
    EventBits_t bits = 0;
    ESP_LOGI(TAG, "waitting wifi group bits");
    // 不清空bit
    bits = xEventGroupWaitBits(s_wifi_event_group, (WIFI_CONNECTED_BIT | WIFI_FAIL_BIT), pdFALSE, pdFALSE, portMAX_DELAY);
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "Connected to SSID:%s, password:%s",
                 wifi_config.sta.ssid, wifi_config.sta.password);
        return CONN_OK;
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 wifi_config.sta.ssid, wifi_config.sta.password);
        return CONN_FAIL;
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
        return CONN_FAIL;
    }
}

void modify_wificonfig_pswd(const char *pswd)
{
    // len of pswd <PSWD_LEN
    strcpy((char *)wifi_config.sta.password, (const char *)pswd);
    printf("\n pswd  is %s \n", wifi_config.sta.password);
}

void modify_wificonfig_ssid(const char *ssid)
{
    // len of pswd <SSID_LEN
    printf("\n get data is %s \n", ssid);
    strcpy((char *)wifi_config.sta.ssid, (const char *)ssid);
    printf("\n ssid  is %s \n", wifi_config.sta.ssid);
}

esp_err_t store_wificonfig_to_nvs(void)
{
    uint8_t ssid[SSID_LEN] = {0};
    uint8_t pswd[PSWD_LEN] = {0};
    strcpy((char *)ssid, (const char *)wifi_config.sta.ssid);
    strcpy((char *)pswd, (const char *)wifi_config.sta.password);

    nvs_handle wifiNvsHandle;
    esp_err_t err;

    err = nvs_open("wifi_config", NVS_READWRITE, &wifiNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
        return err;
    }
    nvs_set_str(wifiNvsHandle, SSID_KEY, (const char *)ssid);
    nvs_set_str(wifiNvsHandle, PSWD_KEY, (const char *)pswd);
    nvs_commit(wifiNvsHandle);
    nvs_close(wifiNvsHandle);
    return ESP_OK;
}

esp_err_t set_wifi_config_from_nvs(void)
{
    uint8_t ssid[SSID_LEN] = {0};
    uint8_t pswd[PSWD_LEN] = {0};
    uint8_t ssid_len = SSID_LEN;
    uint8_t pswd_len = PSWD_LEN;
    nvs_handle wifiNvsHandle;

    esp_err_t err;

    err = nvs_open("wifi_config", NVS_READWRITE, &wifiNvsHandle);
    if (err != ESP_OK)
    {
        printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
        return err;
    }

    err = nvs_get_str(wifiNvsHandle, SSID_KEY, (char *)ssid, &ssid_len);
    if (err != ESP_OK)
    {
        printf("get ssid failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    printf("get ssid = %s \n", ssid);
    err = nvs_get_str(wifiNvsHandle, PSWD_KEY, (char *)pswd, &pswd_len);
    if (err != ESP_OK)
    {
        printf("get pswd failed, err(%s)\n", esp_err_to_name(err));
        return err;
    }
    printf("get pswd = %s \n", pswd);

    strcpy((char *)wifi_config.sta.ssid, (const char *)ssid);
    strcpy((char *)wifi_config.sta.password, (const char *)pswd);

    ESP_LOGI(TAG, " SSID:%s, \n password:%s \n",
             wifi_config.sta.ssid, wifi_config.sta.password);

    nvs_commit(wifiNvsHandle);
    nvs_close(wifiNvsHandle);
    return ESP_OK;
}